/*
 * Created by IntelliJ IDEA.
 * User: rousseau
 * Date: Apr 20, 2002
 * Time: 4:07:08 PM
 * To change template for new class use
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package org.fractals;

import java.util.Arrays;

import org.jfract.abstractBeans.AbstractFractalBean;
import org.jfract.util.CompletionListener;
import org.jfract.util.Location;

import android.content.Context;

/**
 * The implementation for mandelbrot
 */
public final class Mandelbrot
    extends AbstractFractalBean
{
  private static final long serialVersionUID = 499412374776545133L;

  public static final int MANDELBROT_DEFAULT = 0;

  public static final int MANDELBROT_CUBIC = 1;

  public static final int MANDELBROT_QUATRIC = 2;

  public static final int MANDELBROT_QUINTIC = 3;

  public static final int MANDELBROT_SEXTIC = 4;

  static
  {
    System.loadLibrary("fractals");
  }

  public String name = "Mandelbrot";
  
  public String expression = null;

  public int getMax()
  {
    return max;
  }

  public void setMax(int max)
  {
    this.max = max;
  }

  private int max = 2;

  public void setPow(int pow)
  {
    this.pow = pow;
  }

  private int pow = 3;

  public int getPow()
  {
    return pow;
  }

  public Mandelbrot(Context context, int type)
  {
    setType(type);
    setConfiguration(context, type);
    setIter(255);
    setColor(COLORWHITE);
    setInternColor(COLORBLACK_TO_RED);
  }
  public Mandelbrot(Context context, String name, String expression, int type, Location location)
  {
    setType(type);
    setLocation(location);
    this.name = name;
    this.expression = expression;
    setConfiguration(context, type);
    setIter(255);
    setColor(COLORWHITE);
    setInternColor(COLORBLACK_TO_RED);
  }

  private int width;

  private int height;

  public int getWidth()
  {
    return width;
  }

  public void setWidth(int width)
  {
    this.width = width;
  }

  public int getHeight()
  {
    return height;
  }

  public void setHeight(int height)
  {
    this.height = height;
  }

  public void compute()
  {

    int totalPixelSize = (int) (getValues().length) / 2;
    int[] alreadyComputed = new int[totalPixelSize];
    Arrays.fill(alreadyComputed, -1);

    // int[] result = new int[getWidth() * getHeight()];
    int[] resultIter = new int[getWidth() * getHeight()];
    ;
    int cut = getMaxArraySize() / getWidth();
    for (int refreshLevel : getRefreshLevels())
    {

      int startHeight = 0;
      int endHeight = Math.min(cut, getHeight());

      while (endHeight <= getHeight() && startHeight < getHeight())
      {
        int partialTotalSize = getWidth() * (endHeight - startHeight);

        // TODO : generate an out of memory, change it.
        float valuePartial[] = new float[partialTotalSize * 2];

        int alreadyComputedPartial[] = new int[partialTotalSize];
        System.arraycopy(getValues(), startHeight * getWidth() * 2, valuePartial, 0, partialTotalSize * 2);
        System.arraycopy(alreadyComputed, startHeight * getWidth(), alreadyComputedPartial, 0, partialTotalSize);
        int resultPartial[] = compute(valuePartial, getIter(), getInternColor().length, alreadyComputedPartial, getCompletionListener(), refreshLevel, getWidth(), getHeight(), startHeight, endHeight, getType());
        int resultIterPartial[] = new int[partialTotalSize];
        alreadyComputedPartial = new int[partialTotalSize];
        System.arraycopy(resultPartial, 0, resultIterPartial, 0, partialTotalSize);
        System.arraycopy(resultPartial, partialTotalSize, alreadyComputedPartial, 0, partialTotalSize);
        synchronized (processor.getThreadLock())
        {
          getCompletionListener().newImageComputed(resultIterPartial, 0, startHeight, getWidth(), endHeight - startHeight, endHeight == getHeight());
          System.gc();
        }
        System.arraycopy(resultIterPartial, 0, resultIter, startHeight, resultIterPartial.length);
        System.arraycopy(alreadyComputedPartial, 0, alreadyComputed, startHeight * getWidth(), alreadyComputedPartial.length);
        startHeight += cut;
        endHeight = Math.min(endHeight + cut, getHeight());
        valuePartial = null;
        alreadyComputedPartial = null;
      }
    }
  }

  public Location getDefaultLocation()
  {
    return new Location((float) 0, (float) 0, (float) .2);
  }

  protected native int[] compute(float[] values, int maxIter,
      int interColorLength, int[] alreayComputed, CompletionListener listener,
      int delta, int width, int height, int startHeightIndex,
      int endHeightIndex, int type);

}
